package hotel.quartz.oversea.job.switchs;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import hotel.base.oversea.constant.ApiConstans;
import hotel.base.oversea.constant.CommonConstants;
import hotel.base.oversea.constant.CommonUrlConstants;
import hotel.base.oversea.constant.SwitchConstants;
import hotel.base.oversea.constant.quartz.UrlSwitchsEnums;
import hotel.base.oversea.utils.Result;
import hotel.quartz.oversea.config.service.RedisService;
import hotel.quartz.oversea.entity.form.JobForm;
import hotel.quartz.oversea.job.base.BaseJob;
import hotel.quartz.oversea.service.JobAndTriggerService;
import hotel.quartz.oversea.util.JobRestTemplateUtils;
import hotel.quartz.oversea.vo.switchs.DltHotelEntityVo;
import hotel.quartz.oversea.vo.switchs.SwitchMasterHotelVo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  Switchs Hotel Sub Create Job 创建子酒店定时器
 * </p>
 *
 * @description: Switchs Hotel Sub Create Job
 * @author: qiuyy
 * @date: 2020-05-26 13:22
 * @version: V1.0
 */
@Slf4j
public class SwitchHotelSubCreateJob implements BaseJob {
    private String jobGroupName = SwitchConstants.JOB_GROUP_NAME_SWITCHS;
    private String jobClassName = this.getClass().getName();
    private String timestamp = "Shsjc"+String.valueOf(System.currentTimeMillis());
    //处理中的母酒店id 缓存在creatingMap中，每一次定时器走完则移除
    private String redisCreateSubHotelCreatingKey = "quartz_switchs_creating_ids";
    private String citySetPageIndex = "quartz_switchs_create_sub_city_set_page_index";

    private String createSubProcessIngRedisHead = "createSubProcessIngRedisHead";

    @Autowired
    private JobAndTriggerService jobAndTriggerService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 创建并获取子酒店(携程接口是支持多个，但是hhb内部包装为每次上送一个母酒店id)
     *
     *
     * 创建子酒店数据定时器，根据母酒店id，且母酒店id为对比过的母酒店id（sys_compare_hotel表）
     * 备注：当前该次定时器正常结束请求后 update数据库该条数据为 is_create_sub = 1 已创建子酒店；移出‘正在执行idsMap’缓存;
     *
     * 定时器时间：6-8秒一次（由于携程接口限制）
     *
     * 涉及接口:
     * 1、对比系统接口：获取对比后的母酒店id（每次一个）（第一次跑的酒店比较多，后面都是少量新增的对比酒店数据）
     * 2、携程接口：创建子酒店根据母酒店id（每次一个母酒店id）（每分钟调用不得超过10次 ）
     * 3、对比系统接口：更新对比酒店为已创建
     *
     *
     * @param jobExecutionContext
     */
    @Override
    @SneakyThrows
    public void execute(JobExecutionContext jobExecutionContext) {

        //redisTemplate.delete(redisCreateSubHotelCreatingKey);
        log.info("Switchs Hotel Sub Create Job {} >>>>>>>> 创建子酒店执行时间: {}", timestamp, DateUtil.now());
      //  String switchsHost = CommonUrlConstants.SWITCHS_HOST;
        String switchsHost= CommonUrlConstants.SWITCHS_ONLINE_HOST;
        String compareHost = CommonUrlConstants.COMPARE_HOST;
        //对比系统接口地址  获取需创建子酒店的母酒店id
        String getOneMasterIdUrl = compareHost + UrlSwitchsEnums.subHotel.GET_ONE_MASTER_ID.getValue() ;
        //携程系统接口地址  创建子酒店
        String createUrl = switchsHost + UrlSwitchsEnums.subHotel.CREATE.getValue();
        //对比系统接口地址  更新为被创建状态
        String updateUrl = compareHost + UrlSwitchsEnums.subHotel.UPDATE_MASTER_COMPARE_IS_CREATE.getValue();

        //对比后的母酒店id，一个
        String masterHotelId = requestGetOneMasterHotelId(getOneMasterIdUrl);
        if(null != masterHotelId){

            String pro = (String)redisTemplate.opsForValue().get(createSubProcessIngRedisHead+masterHotelId);
            if(null != pro){
                log.info("Switchs Hotel Sub Create Job {} >>>>>>>> 创建子酒店,,当前酒店已存在在执行的线程，跳过: {}，{}",timestamp,pro,createSubProcessIngRedisHead+masterHotelId);
            }else{
                //预先加入缓存map，当前masterId被线程处理，后面定时器执行完后要移出
                //addCreatingMap(masterHotelId);
                redisTemplate.opsForValue().set(createSubProcessIngRedisHead+masterHotelId,"1",5L, TimeUnit.SECONDS);
                //创建子酒店
                requestCreateSubHotel(createUrl,updateUrl,masterHotelId);
            }
        }
        log.info("Switchs Hotel Sub Create Job {} >>>>>>>> 创建子酒店结束时间: {}",timestamp, DateUtil.now());

    }

    /**
     * 请求内部接口 获取 未创建子酒店的 母酒店id
     */
    public String requestGetOneMasterHotelId(String getOneMasterIdUrl){
        Map<String,Integer> map = new HashMap();
        map.put("index",getCitySetPageIndex());
        map.put("pageSize",1);
        log.info("Switchs Hotel Sub Create Job {} 创建子酒店定时器进入方法 requestGetOneMasterHotelId 请求：{}",timestamp, JSON.toJSONString(map));
        //发起请求
        Result response = JobRestTemplateUtils.post(getOneMasterIdUrl, JSON.toJSONString(map),restTemplate);
        log.info("Switchs Hotel Sub Create Job {} 创建子酒店定时器进入方法 requestGetOneMasterHotelId 响应:{}",timestamp, JSON.toJSONString(response));
        if(response.getCode()== CommonConstants.SUCCESS){
            if(null!= response.getData()){
                Map resultMap = (Map)response.getData();
                if(null != resultMap){
                    String masterId = (String)resultMap.get("masterId");
                    Integer index = (Integer)resultMap.get("index");
                    String type = (String)resultMap.get("type");
                    if(null != type && type.equals("1")){//数据来源为对比酒店表
                    }else{//数据来源为配置表，需要index参数
                        setCitySetPageIndex(String.valueOf(index));
                    }
                    if(null != masterId && !"null".equals(masterId)){
                        if(null == redisTemplate.opsForValue().get(createSubProcessIngRedisHead+masterId)){
                            return masterId;
                        }else{
                            return null;
                        }
                    }else{
                        return null;
                    }
                }else{
                    endingJob();
                }
            }else{
                //已无‘需创建子酒店’的母酒店id（对比后的母酒店id），结束定时器

                //不存在，暂停定时器
                endingJob();

                /*//判断是否 缓存中还存在 处理中的母酒店id，存在则表示还有线程没有走完，则不暂停定时器
                if(getCreatingMap().size()>0){
                    //存在线程未释放的处理中的母酒店id，不暂停定时器
                }else{
                    //不存在，暂停定时器
                    endingJob();
                }*/
            }
        }else{
            log.error("Switchs Hotel Sub Create Job {} 创建子酒店定时器 获取母酒店id requestGetOneMasterHotelId  返回失败: {}",timestamp,response);
        }
        return null;
    }

    private void setCitySetPageIndex(String index) {
        redisTemplate.opsForValue().set(citySetPageIndex,index,30L,TimeUnit.MINUTES);
        log.info("Switchs Hotel Sub Create Job {} 创建子酒店定时器 index set:{}",timestamp,index);
    }

    private Integer getCitySetPageIndex() {
        String index = (String)redisTemplate.opsForValue().get(citySetPageIndex);
        if(null == index || index.equals("null")){
            return 0;
        }
        log.info("Switchs Hotel Sub Create Job {} 创建子酒店定时器 index:{}",timestamp,index);
        return Integer.valueOf(index);
    }

    /**
     * 调用携程创建子酒店接口
     * @param url
     * @return
     */
    private void requestCreateSubHotel(String url,String updateUrl,String masterHotelId) {
        Map map = new HashMap();
        map.put("masterHotelId",masterHotelId);
        log.info("Switchs Hotel Sub Create Job {} 创建子酒店信息进入方法 requestCreateSubHotel:{}",timestamp, JSON.toJSONString(map));
        //发起请求
        Result response = JobRestTemplateUtils.post(url, JSON.toJSONString(map),restTemplate);
        if(response.getCode()!= CommonConstants.SUCCESS && null != response.getMsg() && response.getMsg().toString().contains("接口认证失败")){
            //当前母酒店创建子酒店为异常失败，让定时器下次进来再调起创建子酒店
        }else{
            requestUpdateIsCreate(updateUrl,response,masterHotelId);
        }
        //处理完，释放
        //removeCreatingMap(masterHotelId);
        redisTemplate.delete(createSubProcessIngRedisHead+masterHotelId);
    }

    private void requestUpdateIsCreate(String updateUrl, Result createResponse, String masterHotelId) {
        //创建返回成功或失败，更新compare_hotel表，当前母酒店改is_create_sub
        Map mapUpdate = new HashMap();
        mapUpdate.put("masterHotelId",masterHotelId);
        String createRemark = (String)createResponse.getMsg();
        if(createResponse.getCode()== CommonConstants.SUCCESS ){
            if(StringUtils.isEmpty(createRemark)){
                createRemark = "创建子订单成功";
            }
            createRemark = DateUtil.now()+"："+createRemark;
            mapUpdate.put("isCreateSubRemark",createRemark);
            mapUpdate.put("isCreateSub","1");//成功
            log.info("Switchs Hotel Sub Create Job {} requestCreateSubHotel 创建成功",timestamp);
        }else{
            //失败 该酒店下次定时器进来不再查询创建，状态改为2（有的母酒店不可创建子酒店）
            if(StringUtils.isEmpty(createRemark)){
                createRemark = "创建子订单失败且上有失败信息返回null";
            }
            createRemark = DateUtil.now()+"："+createRemark;
            mapUpdate.put("isCreateSubRemark",createRemark);
            mapUpdate.put("isCreateSub","2");
            log.info("Switchs Hotel Sub Create Job {} requestCreateSubHotel 该母酒店不可创建子酒店，接口返回失败，下一次定时器跳过 {},{}",timestamp, JSON.toJSONString(mapUpdate),createResponse);
        }

        //发起请求
        Result updateResponse = JobRestTemplateUtils.get(updateUrl,mapUpdate,restTemplate);
        if(updateResponse.getCode()== CommonConstants.SUCCESS){

        }else{
            log.error("Switchs Hotel Sub Create Job {} 更新母酒店为已创建状态 失败: {}",timestamp, JSON.toJSONString(updateResponse));
        }
    }

    /*public void removeCreatingMap(String masterId){
        Map<String,String> map = (Map<String,String>)redisTemplate.opsForValue().get(redisCreateSubHotelCreatingKey);
        if(null != map && map.size()>0){
            map.remove(masterId);
            redisTemplate.opsForValue().set(redisCreateSubHotelCreatingKey,map);
        }
    }
*/
    /*public Map<String,String> getCreatingMap(){
        Map<String,String> map = (Map<String,String>)redisTemplate.opsForValue().get(redisCreateSubHotelCreatingKey);
        if(null == map){
            map = new HashMap<>();
        }
        return map;
    }*/

    /*public boolean exisCreatingMap(String masterId){
        Map<String,String> map = (Map<String,String>)redisTemplate.opsForValue().get(redisCreateSubHotelCreatingKey);
        if(null != map && map.size()>0){
            if(!StringUtils.isEmpty(map.get(masterId))){
                log.error("Switchs Hotel Sub Create Job {} 创建子酒店 上一个线程还未释放该masterId：{}",timestamp,masterId);
                return true;
            }
        }
        return false;
    }*/

    /*public void addCreatingMap(String masterId){
        Map<String,String> map = (Map<String,String>)redisTemplate.opsForValue().get(redisCreateSubHotelCreatingKey);
        if(null != map){
            map.put(masterId,masterId);
            redisTemplate.opsForValue().set(redisCreateSubHotelCreatingKey,map);
        }else{
            map = new HashMap<>();
            map.put(masterId,masterId);
            redisTemplate.opsForValue().set(redisCreateSubHotelCreatingKey,map);
        }
    }
*/
    /**
     * 无数据 结束定时器
     */
    public void endingJob(){
        redisTemplate.delete(citySetPageIndex);
        //已最后一页，暂停定时器,删除rediskey
        JobForm jobForm = new JobForm();
        jobForm.setJobClassName(jobClassName);
        jobForm.setJobGroupName(jobGroupName);
        try {
            redisTemplate.delete(citySetPageIndex);
            redisTemplate.delete(redisCreateSubHotelCreatingKey);
            jobAndTriggerService.pauseJob(jobForm);
            log.info("Switchs Hotel Sub Create Job {} 创建子酒店定时器执行暂停！: {},{}",timestamp,jobGroupName,jobClassName);
        } catch (SchedulerException e) {
            log.error(e.getMessage(),e);
        }
        String next = (String)redisTemplate.opsForValue().get("create_sub_hotel_next_get_sub_hotel_details");
        log.info("Switchs Hotel Sub Create Job {}  判断是否需要直接调用拉取酒店信息！next={}",timestamp,next);
        if(null == next){
            nextGetCreateSubHotelDetail();
        }
    }





    @Autowired
    private RedisService redisService;
    private String redisSwChildIdsPageIndex = SwitchConstants.REDIS_SWITCH_HOTEL_CHILD_IDS_PAGE_INDEX;
    private  int pageSize = ApiConstans.SW_PAGE_SIZE;
    private int thisRedisCount = SwitchConstants.REDIS_SW_HOTEL_CHILD_INFOS_WAIT_COUNT;
    private String redisWaitSaveKey = SwitchConstants.REDIS_SW_HOTEL_CHILD_INFOS_WAIT_SAVE;


    public void nextGetCreateSubHotelDetail(){
        redisTemplate.opsForValue().set("create_sub_hotel_next_get_sub_hotel_details",1,2L,TimeUnit.SECONDS);
        log.info("Switchs Hotel Sub Job {} >>>>>>>> 拉取子酒店详情接口调用，携程子酒店信息获取执行时间: {}", timestamp, DateUtil.now());
        String host = CommonUrlConstants.SWITCHS_HOST;
        String childHotelUrl = host + UrlSwitchsEnums.subHotel.GET.getValue();
        String saveHotelUrl = host + UrlSwitchsEnums.subHotel.SAVE.getValue();
        boolean check = checkSaveRedisData(saveHotelUrl);
        if(!check){
            //当前需查询的城市的酒店第三方接口页码pageIndex
            String pageIndex = getChildHotelPageIndex();
            Map<String,Object> params = new HashMap();
            params.put("masterHotelID",0);
            params.put("hotelID",0);
            params.put("pageIndex",Integer.valueOf(pageIndex));
            params.put("pageSize",pageSize);
            requestChildHotelDataPage(JSON.toJSONString(params),pageIndex,childHotelUrl,saveHotelUrl);
        }

        //log.info("Switchs Hotel Sub Job {} >>>>>>>> 携程子酒店信息获取结束时间: {}",timestamp,DateUtil.now());
    }

    /**
     * 校验是否需要保存缓存的数据
     * @param saveUrl
     * @return
     */
    private boolean checkSaveRedisData(String saveUrl) {
        log.info("Switchs Hotel Sub Job {} 携程子酒店信息进入方法 checkSaveRedisData",timestamp);
        Map<String,List<DltHotelEntityVo>> getInfos = getRedisWaitData();
        if(null != getInfos && getInfos.size()>=thisRedisCount){
            //缓存酒店数据达到大于等于thisRedisCount，则执行保存到数据库操作，可能大于thisRedisCount比如数据操作保存失败还原加入到redis
            boolean result = requestSaveWaitDataApi(saveUrl,getInfos);
            if(result){
                return true;
            }else{
                return false;
            }
        }else{
            return false;
        }
    }

    /**
     * 调用保存缓存数据集合接口
     * @param saveUrl
     * @param redisWaitDataSaveMap
     * @return
     */
    private boolean requestSaveWaitDataApi(String saveUrl,Map<String, List<DltHotelEntityVo>> redisWaitDataSaveMap) {
        if(null == redisWaitDataSaveMap || redisWaitDataSaveMap.size()==0){
            log.warn("Switchs Hotel Sub Job {} 携程子酒店信息进入方法 requestSaveWaitDataApi 待保存数据redisWaitDataSaveMap为空",timestamp);
            return true;
        }
        log.info("Switchs Hotel Sub Job {} 携程子酒店信息进入方法 requestSaveWaitDataApi",timestamp);
        Map map = new HashMap();
        map.put("switchHotelInfoVosMap",redisWaitDataSaveMap);
        //调用前,删除当前缓存的酒店数据，让下一次定时器正常查询上游接口。若调用结果失败则还原加入缓存
        deleteRedisWaitSaveData();
        //发起请求
        Result response = JobRestTemplateUtils.post(saveUrl, JSON.toJSONString(map),restTemplate);
        if(response.getCode()== CommonConstants.SUCCESS){
            log.info("Switchs Hotel Sub Job {} requestSaveWaitDataApi 保存数据成功",timestamp);
            return true;
        }else{
            log.error("Switchs Hotel Sub Job {} requestSaveWaitDataApi 异常错误: {}",timestamp,response);
            backPutRedisWaitSaveData(redisWaitDataSaveMap);
            return false;
        }
    }
    /**
     * 删除缓存的待保存数据
     * @return
     */
    public void deleteRedisWaitSaveData(){
        log.info("Switchs Hotel Sub Job {} 携程子酒店信息进入方法 deleteRedisWaitSaveData 删除当前携程子酒店信息数据缓存",timestamp);
        redisTemplate.delete(redisWaitSaveKey);
    }
    /**
     * 还原追加回去待保存数据
     * @return
     */
    public void backPutRedisWaitSaveData(Map<String,List<DltHotelEntityVo>> redisWaitDataMap){
        log.info("Switchs Hotel Sub Job {} 携程子酒店信息进入方法 backPutRedisWaitSaveData 还原追加当前酒店信息缓存 ",timestamp);
        //还原redis数据
        Map<String,List<DltHotelEntityVo>> getRedisMap = (Map<String,List<DltHotelEntityVo>>)redisService.get(redisWaitSaveKey);
        if(null != getRedisMap && getRedisMap.size()>0){
            getRedisMap.putAll(redisWaitDataMap);
        }else{
            getRedisMap = redisWaitDataMap;
        }
        redisService.set(redisWaitSaveKey,getRedisMap);
    }

    /**
     * 获取保存在缓存中的待save的数据
     */
    private Map<String,List<DltHotelEntityVo>> getRedisWaitData() {
        return (Map<String,List<DltHotelEntityVo>>)redisService.get(redisWaitSaveKey);
    }

    /**
     * 查询指定城市的某些酒店列表数据：根据子酒店，页码index定位
     */
    public void requestChildHotelDataPage(String paramsJson,String pageIndex,String cityHotelUrl,String saveUrl){
        log.info("Switchs Hotel Sub Job {} 携程子酒店信息进入方法 requestChildHotelDataPage: {}",timestamp,paramsJson);
        //发起请求
        Result response = JobRestTemplateUtils.post(cityHotelUrl,paramsJson,restTemplate);
        if(response.getCode()== CommonConstants.SUCCESS){
            if(null!= response.getData() && null != JSON.parseObject(JSON.toJSONString(response.getData())).getString("dltHotelEntityList")){
                //解析响应结果
                String datajson = JSON.toJSONString(response.getData());
                String hotelStr = JSON.parseObject(datajson).getString("dltHotelEntityList");
                List<SwitchMasterHotelVo> hotelData = (List<SwitchMasterHotelVo>) JSON.parseObject(hotelStr,List.class);
                if(null == hotelData || hotelData.size()==0){
                    //当前页面pageIndex查询结果无数据
                    //无数据也没下一页，redis数据操作保存，保存成功后暂停定时器
                    successUpdateSaveEnding(saveUrl);
                }else{
                    //查询到数据，存入redis
                    Map dataMap = new HashMap();
                    dataMap.put(UUID.randomUUID().toString(),hotelData);
                    boolean setResultRedis = savePutRedisWaitData(dataMap);
                    if(setResultRedis){
                        //只要有数据就定义为还有下一页，更新redis城市index
                        int newIndex = Integer.valueOf(pageIndex)+1;
                        setChildHotelPageIndex(String.valueOf(newIndex));
                        log.error("Switchs Hotel Sub Job {} 携程子酒店信息 拉取详情 暂停5秒",timestamp);
                        try {
                            Thread.sleep(5000);
                        } catch (Exception e) {
                            log.error(e.getMessage(), e);
                        }
                        nextGetCreateSubHotelDetail();//循环调用
                    }else{
                        //更新失败，不更新标识，下一次定时器继续查询当前
                        log.error("Switchs Hotel Sub Job {} 携程子酒店信息 setResultRedis 异常错误: {},{}",timestamp,response,paramsJson);
                    }
                }
            }else{
                //当前页面pageIndex查询结果无数据
                //无数据也没下一页，redis数据操作保存，保存成功后暂停定时器
                successUpdateSaveEnding(saveUrl);
            }
        }else{
            //异常错误，redis子酒店无变化，定时器下次进入继续查询当前酒店
            //endingJob(false);
            log.error("Switchs Hotel Sub Job {} 获取携程子酒店信息 返回失败: {},{}",timestamp,response,paramsJson);
        }
    }
    /**
     * 成功请求酒店房型接口后，合法酒店房型数据，追加进缓存中
     */
    private boolean savePutRedisWaitData(Map<String, List<Long>> mapRealRoomVoMap) {
        Map<String,List<Long>> getRedisMap = (Map<String,List<Long>>)redisService.get(redisWaitSaveKey);
        if(null != getRedisMap && getRedisMap.size()>0){
            getRedisMap.putAll(mapRealRoomVoMap);
        }else{
            getRedisMap = mapRealRoomVoMap;
        }
        boolean result = redisService.set(redisWaitSaveKey,getRedisMap);
        return result;
    }
    /**
     * 最后一页 更新掉剩余缓存待保存的数据，结束定时器
     */
    public void successUpdateSaveEnding(String saveUrl){
        /*boolean re = requestSaveWaitDataApi(saveUrl,getRedisWaitData());
        if(re){
            //已最后一页，暂停定时器,删除rediskey
            JobForm jobForm = new JobForm();
            jobForm.setJobClassName(jobClassName);
            jobForm.setJobGroupName(jobGroupName);
            try {
                jobAndTriggerService.pauseJob(jobForm);
                deleteRedisChildHotelIndex();
                log.warn("Switchs Hotel Sub Job {} 携程子酒店定时器执行暂停！: {},{}",timestamp,jobGroupName,jobClassName);
            } catch (SchedulerException e) {
                log.error(e.getMessage(),e);
            }
        }else{
            log.error("Switchs Hotel Sub Job {} 最后一页保存操作 异常错误: {},{}",timestamp);
        }*/

        log.info("Switchs Hotel Sub Job {} >>>>>>>> 携程子酒店信息获取结束时间: {}",timestamp, DateUtil.now());
    }

    public String getChildHotelPageIndex(){
        String index = (String)redisTemplate.opsForValue().get(redisSwChildIdsPageIndex);
        if(null == index){
            index  = "1";//接口参数要求  大于0
        }
        return index;
    }
    public void setChildHotelPageIndex(String newIndex){
        redisTemplate.opsForValue().set(redisSwChildIdsPageIndex,newIndex);
    }
    public void deleteRedisChildHotelIndex(){
        redisTemplate.delete(redisSwChildIdsPageIndex);
    }


}
